OpenGL ES

您所在的位置:网站首页 opengl library OpenGL ES

OpenGL ES

#OpenGL ES| 来源: 网络整理| 查看: 265

一、OpenGL ES介绍

OpenGL(Open Graphics Library)定义了一个跨编程语言、跨平台编程的专业图形程序接口。可用于二维或三维图像的处理和渲染,它是一个功能强大、调用方便的底层图形库。对于嵌入式设备,其提供了OpenGL ES(OpenGL for Embeddled Systems)版本,该版本是针对手机、Pad等嵌入式设备而设计的,是OpenGL的一个子集。到目前为止,OpenGL已经经历过很多版本的迭代与更新,最新版本是3.0,而使用最广泛的还是OpenGL ES 2.0版本。本文是基于2.0版本进行编程并实现图像的处理与渲染,并且只讨论2D部分的内容。

由于OpenGL是基于跨平台设计的,所以在每个平台上都要有它的具体实现,即要提供OpenGL ES的上下文环境以及窗口的管理。在OpenGL的设计中,OpenGL是不负责管理窗口的,窗口的管理将交由各个设备自己来完成,上下文环境也是一样的,其在各个平台上都有自己的实现。在iOS平台上使用EAGL提供本地平台对OpenGL ES的实现。

这里需要介绍一个库——libSDL,它可以为开发者提供面向libSDL的API编程,libSDL内部会解决多个平台的OpenGL上下文环境以及窗口的管理问题,开发者只需要交叉编译这个库到各自的平台上就可以做到只写一份代码即可运行多个平台。其中FFmpeg中的ffplay这一工具就是基于libSDL进行开发的。但是对于移动开发者来讲,这样就会失去一些更加灵活的控制,甚至某些场景下的功能不能实现。

上面介绍了OpenGL ES是什么,下面再来介绍一下OpenGL ES能做什么。其实从名字上就可以看出来,OpenGL主要是做图形图像处理的库,尤其是在移动设备上进行图形图像处理,它的性能优势更能体现出来。GLSL(OpenGL Shading Language)是OpenGL的着色器语言,开发人员利用这种语言编写程序运行在GPU(Graphic Processor Unit,图形图像处理单元,可以理解为是一种高并发的运算器)上以进行图像的处理和渲染。GLSL着色器代码分为两个部分,即Vertex Shader(顶点着色器)与Fragment Shade(片元着色器)两部分,分别完成各自在OpenGL渲染管线中的功能。对于OpenGL ES,业界有一个著名的开源库GPUImage,它的实现非常优雅,尤其是在iOS平台上实现的非常完备,不仅有摄像头采集实时渲染、视频播放器、离线保存等功能,更有强大的滤镜实现。在GPUImage的滤镜实现中,可以找到大部分图形图像处理Shader的实现,包括:亮度、对比度、饱和度、色调曲线、白平衡、灰度等调整颜色的处理,以及锐化、高斯模糊等图像像素处理的实现等,还有素描、卡通效果、浮雕效果等视觉效果的实现,最后还有各种混合模式的实现等。当然除了GPUImage提供的这些图像处理的Shader之外,开发者也可以自己实现一些有意思的Shader,比如美颜滤镜效果、瘦脸效果以及粒子效果等。

二、OpenGL ES的实践 1.OpenGL 渲染管线

要想学习着色器,并理解着色器的工作机制,就要对OpenGL 固定的渲染管线有深入的了解。同样,先来统一一下术语。

几何图元:包括点、直线、三角形,均是通过顶点(vertex)来指定的。 模型:根据几何图元创建的物体。 渲染:计算机根据模型创建图像的过程。

最终渲染过程结束之后,人眼所能看到的图像就是由屏幕上的所有像素点组成的,在内存中,这些像素点可以组织成一个大的一维数组,每4个字节即表示一个像素点的RGBA数据,而在显卡中,这些像素点可以组织成帧缓冲区的形式,帧缓冲区保存了图形硬件为了控制屏幕上所有像素的颜色和强度所需要的全部信息。理解了帧缓冲区的概念,接下来就来讨论一下OpenGL的渲染管线,这部分内容对于OpenGL来说是非常重要的。

那么OpenGL的渲染管线具体是做什么的呢?其实就是OpenGL引擎渲染图像的流程,也就是说OpenGL引擎是一步一步地将图片渲染到屏幕上去的过程。渲染管线分为以下几个阶段。

阶段一:指定几何对象

所谓几何对象,就是上面说过的几何图元,这里将根据具体执行的指令绘制几何图元。比如,OpenGL提供给开发者的绘制方法glDrawArrays,这个方法里的第一个参数是mode,就是制定绘制方式,可选值有一下几种。

GL_POINT:以点的形式进行绘制,通常用在绘制粒子效果的场景中。 GL_LINES:以线的形式进行绘制,通常用在绘制直线的场景中。 GL_TRIANGLE_STRIP:以三角形的形式进行绘制,所有二维图像的渲染都会使用这种方式。

具体选用哪一种绘制方式决定了OpenGL渲染管线的第一阶段应如何去绘制几何图元,所以这就是第一阶段指定的几何对象。

阶段二:顶点处理

不论以上的几何对象是如何指定的,所有的几何数据都将会经过这个阶段。这个阶段所做的操作就是,根据模型视图和投影矩阵进行变换来改变顶点的位置,根据纹理坐标与纹理矩阵来改变纹理坐标的位置,如果涉及三维的渲染,那么这里还要处理光照计算与法线变换。这里的输出是以gl_Position来表示具体的顶点位置的,如果是以点来绘制几何图元,那么还应该输出gl_PointSize。

阶段三:图元组装

在经过阶段二的顶点处理操作之后,还是纹理坐标都是已经确定好了的。在这个阶段,顶点将会根据应用程序送往图元的规则(如GL_POINT、GL_TRIANGLE_STRIP),将纹理组装成图元。

阶段四:栅格化操作

由阶段三传递过来的图元数据,在此将会分解成更小的单元并对应于帧缓冲区的各个像素。这些单元称为片元,一个片元可能包含窗口颜色、纹理坐标等属性。片元的属性是根据顶点坐标利用插值来确定的,这就是栅格化操作,也就是确认好每一个片元是什么。

阶段五:片元处理

通过纹理坐标取得纹理(texture)中相对应的片元像素值(texel),根据自己的业务处理(比如提亮、饱和度调节、对比度调节、高斯模糊)来变换这个片元的颜色。这里的输出是gl_FragColor,用于表示修改之后的像素的最终结果。

阶段六:帧缓冲操作

该阶段主要执行帧缓冲的写入操作,这也是渲染管线的最后一步,负责将最终的像素值写入到帧缓冲区中。

前面也提到过,OpenGL ES提供了可编程的着色器来代替渲染管线的某个阶段。具体如下所示: Vertex Shader(顶点着色器)用来替代顶点处理阶段。 Fragment Shader(片元着色器,又称为像素着色器)用来替换片元处理阶段。

glFinish和glFlush

提交给OpenGL的绘图指令并不会马上发送给图形硬件执行,而是放到一个缓冲区里面,等待缓冲区满了之后再将这些指令发送给图形硬件执行,所以指令较少或较简单时是无法填充缓冲区的,这些指令自然不能马上执行以达到所需要的效果。因此每次写完绘图代码,需要让其立即完成效果时,开发者需要在代码后面添加glFlush()或glFinish()函数。

glFlush()的作用是将缓冲区中的指令(无论是否为满)立即发送给图形硬件执行,发送完立即返回。 glFinish()的作用也是将缓冲区中的指令(无论是否为满)立即发送给图形硬件执行,但是要等待图形硬件执行完成之后才返回这些指令。 2.GLSL语法与内建函数

GLSL是为了实现着色器的功能而向开发人员提供的一种开发语言。

(1)GLSL的修饰符与基本数据类型

具体来说,GLSL的语法与C语言非常类似,学习一门语言,首先要看它的数据类型表示,然后再学习具体的运行流程。对于GLSL,其数据类型表示具体如下.

修饰符

具体如下:

const:用于声明非可写的编译时常量变量。 attribute:用于经常更改的信息,只能在顶点着色器中使用。 uniform:用于不经常更改的信息,可用于顶点着色器和片元着色器。 varying:用于修饰从顶点着色器向片元着色器传递的变量 基本数据类型

int、float、bool,这些与C语言都是一致的,需要强调的一点就是,这里面的float是有一个修饰符的,即可以指定精度。三种修饰符的范围(范围一般视显卡而定)和应用情况具体如下。

highp:32bit,一般用于顶点坐标(vertex Coordinate)。 medium:16bit,一般用于纹理坐标(texure Coordinate)。 lowp:8bit,一般用于颜色显示(color)。 向量类型

向量类型是Shader中非常重要的一个数据类型,因为在做数据传递的时候需要经常传递多个参数,相较于写多个基本数据类型,使用向量类型是非常好的选择。列举一个最经典的例子,要将物体坐标和纹理坐标传递到Vertex Shader中,用的就是向量类型,每一个顶点就是一个四维向量,在Vertex Shader中利用这两个四维向量即可完成自己的纹理坐标映射操作。声明方式如下(GLSL代码):

attribute vec4 position; 矩阵类型

有一些效果器需要开发者传入矩阵类型的数据,比如后面会接触到的怀旧效果器,就需要传入一个矩阵来改变原始的像素数据。声明方式如下:

uniform lowp mat4 colorMatrix;

上面的代码表示了一个4x4的浮点矩阵,如果是mat2就是2x2的浮点矩阵,如果是mat3就是3x3的浮点矩阵。若要传递一个矩阵到实际的Shade中,则可以直接调用如下函数:

glUniformMarix4fv(mColorMatrixLocation,1,false,mColorMatrix); 纹理类型

一般仅在Fragment Shader中使用这个类型,二维纹理的声明方式如下

uniform sample2D texSampler;

当客户端接收到这个句柄时,就可以为它绑定一个纹理,代码如下:

glActiveTexture(GL_TEXTURE0); glBindTexture(GL_TEXTURE_2D,texId); glUniformli(mGLUniformTexture,0);

上述代码第一行激活的是哪一个纹理句柄,第三行代码中的第二个参数需要传递对应的Index,就像代码中激活的纹理句柄是GL_TEXTURE0,对应的Index就是0,如果激活的纹理句柄是GL_TEXTURE1,那么对应的Index就是1,在不同的平台上句柄的个数也是不一样,但是一般都会在32个以上。

varying

这个修饰符变量用于在Vertex Shader和Fragment Shader之间传递函数。首先在顶点着色器中声明这个类型的变量代表纹理的坐标点,并且对这个变量进行赋值,代码如下:

attribute vec2 texoord; varying vec2 v_texcoord; void main(void) { // 计算顶点坐标 v_texcoord = texcoord; }

紧接着在Fragment Shader中也声明同名的变量,然后使用texture2D方法取出二维纹理中该纹理坐标点上的纹理像素值,代码如下

varying vec2 v_texcoord; vec4 texel = texture2D(texSampler,v_texcoord);

取出了该坐标点上的像素值之后,就可以进行像素变化操作了,比如说提高对比度,最终将改变的像素值复制给gl_FragColor。

(2)GLSL的内置函数与内置变量

首先来看内置变量,最常见的是两个Shader的输出变量。 先来看Vertex Shader的内置变量:

vec4 gl_position;

上述代码用来设置顶点转换到屏幕坐标的位置,Vertex Shader一定要去更新这个数值。另外还有一个内置变量,

float gl_pointSize;

在粒子效果的场景下,需要为粒子设置大小,改变该内置变量的值就是为了设置每一个粒子矩形的大小。 其次是Fragment Shader的内置变量,代码如下

vec4 gl_FragColor;

上述代码用于指定当前纹理坐标所代表的像素点的最终颜色值。

然后是内置函数,具体的函数可以去官方文档中查询,这里仅介绍几个常用的函数。

abs(genType x):绝对值函数。

floor(genType x):向下取整函数。

ceil(genType x):向上取整函数。

mod(genType x,genType y):取模函数。

min(genType x,genType y):取的最小值函数。

max(genType x,genType y):取得最大值函数。

clamp(genType x,genType y,genType z):取得中间值函数。

step(genType x,genType y):如果x



【本文地址】


今日新闻


推荐新闻


CopyRight 2018-2019 办公设备维修网 版权所有 豫ICP备15022753号-3